<template>
  <div class="container">
    <div class="main-box">
      <el-card class="left-stream-card">
        <div class="room-header">
          <el-avatar :size="60" :src="nowItem.avatar" alt=""></el-avatar>
          <div class="room-info">
            <div class="room-title">{{ nowItem.title }}</div>
            <div class="room-creator">{{ nowItem.username }}</div>
          </div>
          <el-button type="danger" size="mini" class="exit-room-btn" @click="jumpToLastPage">退出直播间</el-button>
        </div>
        <div class="stream-area">
          <div class="without-stream-ui">
            <div class="description-text">主播当前暂未开启摄像头或屏幕共享</div>
          </div>
          <div :id="'user_' + nowUser.id" class="inner-stream-area"></div>
          <canvas class="barrage-canvas"></canvas>
        </div>
        <div class="operate-area" v-if="isUserCanOperate">
          <div class="operate-item" @click="operateVideo">
            <img src="../assets/image/camera_close.png" alt="" v-if="isVideoOpen === false" class="operate-item-icon"/>
            <img src="../assets/image/camera_open.png" alt="" class="operate-item-icon" v-else/>
            <div class="operate-item-name">{{ isVideoOpen === true ? '关闭' : '开启' }}摄像头</div>
          </div>
          <div class="operate-item" style="margin-left: 10px" @click="operateMicrophone">
            <img src="../assets/image/microphone_close.png" alt="" v-if="isMicrophoneOpen === false"
                 class="operate-item-icon"/>
            <img src="../assets/image/microphone_open.png" alt="" class="operate-item-icon" v-else/>
            <div class="operate-item-name">{{ isMicrophoneOpen === true ? '关闭' : '开启' }}麦克风</div>
          </div>
          <div class="operate-item" style="margin-left: 10px" @click="operateShareScreen">
            <img src="../assets/image/share_screen_close.png" alt="" v-if="isShareScreenOpen === false"
                 class="operate-item-icon"/>
            <img src="../assets/image/share_screen_open.png" alt="" class="operate-item-icon" v-else/>
            <div class="operate-item-name">{{ isShareScreenOpen === true ? '关闭' : '开启' }}屏幕共享</div>
          </div>
        </div>
      </el-card>
      <div class="width-blank"></div>
      <el-card class="right-chat-card">
        <div class="chat-header">
          <div>实时聊天</div>
          <el-link :underline="false" type="primary" v-if="this.nowItem.username === this.nowUser.username"
                   class="listener-list" @click="getListenerList">观众列表
          </el-link>
        </div>
        <div class="chat-content" v-if="chatHistory.length > 0">
          <div class="single-chat" v-for="(item,index) in chatHistory" :key="index" v-html="item"></div>
        </div>
        <div class="chat-content" v-else>
          <el-empty description="好像还没有人发送聊天哦。" style="width: 100%;height: 100%"></el-empty>
        </div>
        <div class="chat-message-input">
          <el-input
              @keydown.enter.native="sendChatMessage"
              resize="none"
              type="textarea"
              :rows="3"
              placeholder="你不说我不说，直播内容哪里来。"
              v-model="chatContent">
          </el-input>
          <el-button type="primary" size="mini" class="send-btn" @click="sendChatMessage">发送</el-button>
        </div>
      </el-card>
    </div>

    <el-dialog
        title="直播间观众列表"
        :visible.sync="showListenerList"
        width="30%">
      <el-table
          :data="roomUserList"
          border
          stripe
          style="width: 100%">
        <el-table-column
            prop="username"
            label="用户名">
        </el-table-column>
        <el-table-column
            fixed="right"
            label="操作">
          <template slot-scope="scope">
            <el-button @click="interactiveChat(scope.row)" type="text" size="small">互动连麦</el-button>
          </template>
        </el-table-column>
      </el-table>
      <span slot="footer" class="dialog-footer">
        <el-button @click="showListenerList = false">关 闭</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import LibGenerateTestUserSig from '../../lib-generate-test-usersig.min'
import TRTC from "trtc-sdk-v5";
import {io} from "socket.io-client";
import {updateRoomView} from "@/api/stream";

export default {
  name: 'LiveQADetail',
  data() {
    return {
      // 所有弹幕
      texts: [],
      canvas: null,
      ctx: null,
      isUserCanOperate: false,
      domId: '',
      socket: null,
      isVideoOpen: false,
      isShareScreenOpen: false,
      isMicrophoneOpen: false,
      nowItem: {},
      nowUser: {},
      chatHistory: [],
      showListenerList: false,
      roomUserList: [],
      // 用户输入的内容
      chatContent: '',
      // 在腾讯云申请的sdk
      sdkAppId: 1600079126,
      sdkSecretKey: '4077032ca29026c7be64ec94570b576df991c08b1d7a64f5a2ed986e48ea88df',
    }
  },
  mounted() {
    this.nowItem = this.$route.query.item
    this.nowUser = this.$route.query.nowUser
    this.domId = 'user_' + this.nowUser.id
    if (this.nowItem.username === this.nowUser.username) {
      this.isUserCanOperate = true
    }
    // 初始化trtc
    this.initTRTC();
    // 初始化socketio
    this.initSocket()
    // 随后进入房间
    this.joinRoom();
  },
  beforeDestroy() {
    this.exitRoom();
  },
  methods: {
    // 初始化trtc
    initTRTC() {
      try {
        this.trtc = TRTC.create();
        console.log("初始化 TRTC 成功");
        this.addEventListeners(); // 注册事件监听器
      } catch (error) {
        console.error("初始化 TRTC 失败: " + error);
      }
    },
    // 初始化socketio
    initSocket() {
      // 和后端连接
      this.socket = io('https://yiyuy.mynatapp.cc/stream');
      this.addSocketListeners();
      // 随后加入房间
      this.socket.emit('joinRoom', {username: this.nowUser.username, roomId: this.nowItem.roomId})
    },
    // 为socketio添加监听器
    addSocketListeners() {
      let that = this;
      this.socket.on('joinRoom', function (message) {
        that.chatHistory.push(message)
      })
      this.socket.on('leaveRoom', function (message) {
        that.socket.disconnect();
        that.socket = null;
      })
      this.socket.on('sendMessage', function (message) {
        that.chatHistory.push(message)
      })
      this.socket.on('getRoomUserList', function (message) {
        console.log(message)
        that.roomUserList = message
      })
      this.socket.on('interactiveChat', function (username) {
        // 判断返回的username和当前username是不是同一个 是就弹出来连麦申请
        if (username !== that.nowUser.username) {
          return;
        }
        const notify = that.$notify({
          title: '连麦申请',
          duration: 5000,
          dangerouslyUseHTMLString: true,
          showClose: false,
          message: '<div><strong>主播申请与你连麦，同意后将获取开启摄像头，麦克风，屏幕共享等权利。</strong></div><div style="display: flex; align-items: center;margin-top: 5px;"><button style="background-color: #409EFF;border-color: #409eff;border-radius: 4px;color: #fff" class="agree-btn">同意</button><button class="disagree-btn" style="margin-left: 10px;background-color: #F56C6C;border-color: #F56C6C;border-radius: 4px;color: #fff">拒绝</button></div>',
        });

        notify.$el.querySelector('.agree-btn').onclick = () => {
          that.switchUserRole()
          that.socket.emit('messageForward', {
            username: that.nowItem.username,
            roomId: that.nowItem.roomId,
            message: '对方同意连麦'
          })
          // 点击后关闭notify 不需要的话可删掉
          notify.close();
        };
        notify.$el.querySelector('.disagree-btn').onclick = () => {
          that.socket.emit('messageForward', {
            username: that.nowItem.username,
            roomId: that.nowItem.roomId,
            message: '对方拒绝了连麦'
          })
          // 点击后关闭notify 不需要的话可删掉
          notify.close();
        };
      })
      this.socket.on('messageForward', function (message) {
        console.log(message)
        if (message.username !== that.nowUser.username) {
          return;
        }

        that.$notify({
          title: '提示',
          message: message.message,
          type: 'success'
        })
      })
    },
    // 为本地trtc添加事件监听器
    addEventListeners() {
      if (this.trtc === null) {
        console.error("TRTC 未初始化");
        return;
      }
      /**
       * 监听 TRTC 的错误事件
       * @param {Object} event 事件对象
       * @param {Number} event.code 错误码
       * @param {Number} event.extraCode 额外错误码
       * 错误事件，非 API 调用错误，SDK 在运行过程中出现了不可恢复的错误时抛出。
       * 错误码(error.code)为：ErrorCode.OPERATION_FAILED
       * 可能的扩展错误码(error.extraCode)：5501, 5502
       */
      this.trtc.on(TRTC.EVENT.ERROR, (event) => {
        const errorCode = event.code;
        const extraCode = event.extraCode;
        console.log(`error code: ${errorCode} extra code: ${extraCode}`);
      });

      /**
       * 监听自动播放失败事件
       * @param {Object} event 事件对象
       * @param {String} event.reason 错误原因
       * @param {String} event.mediaType 媒体类型
       * @param {String} event.streamId 流 ID
       * @param {String} event.userId 用户 ID
       * @param {String} event.extraInfo 额外信息
       * 自动播放失败
       */
      // 监听自动播放失败事件
      this.trtc.on(TRTC.EVENT.AUTOPLAY_FAILED, (event) => {
        console.log("自动播放失败", event);
        // 引导用户点击页面，可以弹窗提示。
      });

      /**
       * 监听用户被踢出房间事件
       * @param {Object} event 事件对象
       * @param {String} event.reason 踢出原因
       * 用户被踢出房间
       * 被踢出房间，原因如下：
       * kick: 相同 userId 的用户同时进入同一个房间（以下简称为同名进房），先进入房间的用户会被后进入的用户踢出房间。
       * 同名进房是不允许的行为，可能会导致双方音视频通话异常，业务侧应避免出现这种情况。
       * TRTC 后台不保证观众角色同名进房互踢。即观众角色的用户，使用同 userId 进同一个房间，可能不会收到该事件。
       * banned: 系统管理员通过服务端 API 将该用户踢出房间。
       * room-disband: 系统管理员通过服务端 API 解散房间。
       */
      this.trtc.on(TRTC.EVENT.KICKED_OUT, (event) => {
        console.log(`用户被踢出房间，原因：${event.reason}`);
        console.log(event.reason);
      });

      /**
       * 监听远端用户进房事件
       * @param {Object} event 事件对象
       * @param {String} event.userId 远端用户的 UserId
       * 远端用户进房事件。
       * rtc 模式下，所有用户都会收到进退房通知。
       * live 模式下，只有主播才有进退房通知，观众没有进退房通知，观众可以收到主播的进退房通知
       */
      this.trtc.on(TRTC.EVENT.REMOTE_USER_ENTER, (event) => {
        console.log(`用户进入房间: ${event.userId}`);
        console.log("用户进入房间: ", event.userId);
        // 处理远端用户进房事件
      });

      /**
       * 监听用户退房
       * @param {Object} event 事件对象
       * @param {String} event.userId 远端用户的 UserId
       * 用户退房事件
       * rtc 模式下，所有用户都会收到进退房通知
       * live 模式下，只有主播才有进退房通知，观众没有进退房通知，观众可以收到主播的进退房通知
       */
      this.trtc.on(TRTC.EVENT.REMOTE_USER_EXIT, (event) => {
        console.log("用户离开房间: ", event.userId);
        // 处理用户退房逻辑
      });

      /**
       * 监听远端用户发布了音频
       * @param {Object} event 事件对象
       * @param {String} event.userId 远端用户的 UserId
       * 远端用户发布了音频
       */
      this.trtc.on(TRTC.EVENT.REMOTE_AUDIO_AVAILABLE, (event) => {
        console.log("远端用户发布了音频: ", event.userId);
      });

      /**
       * 监听远端用户停止发布了音频
       * @param {Object} event 事件对象
       * @param {String} event.userId 远端用户的 UserId
       * 远端用户停止发布了音频
       */
      this.trtc.on(TRTC.EVENT.REMOTE_AUDIO_UNAVAILABLE, (event) => {
        console.log("远端用户停止发布了音频: ", event.userId);
      });

      /**
       * 监听远端用户发布了视频
       * @param {Object} event 事件对象
       * @param {String} event.userId 远端用户的 UserId
       * 远端用户发布了视频
       * 该事件意味着远端用户已经发布了视频流，
       * 但这不意味着你可以立即播放视频，
       * 你需要等待 TRTC.EVENT.REMOTE_VIDEO_PLAYING 事件
       * 才能播放视频
       */
      this.trtc.on(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, (event) => {
        console.log("远端用户发布了视频: ", event.userId);
        // 根据监听到的内容类型做判断 main-用户开启摄像头并发布
        let tipText = ''
        let withoutStreamUI = document.querySelector('.without-stream-ui')
        if (event.streamType === "main") {
          tipText = '主播开启了摄像头'
        }
        withoutStreamUI.style.display = 'none'
        this.$message.success(tipText)
        // 开始播放远端视频
        const config = {
          userId: event.userId,
          streamType: event.streamType,
          view: this.domId
        }
        this.trtc.startRemoteVideo(config);
      });

      /**
       * 监听远端用户停止发布了视频
       * @param {Object} event 事件对象
       * @param {String} event.userId 远端用户的 UserId
       * 远端用户停止发布了视频
       */
      this.trtc.on(TRTC.EVENT.REMOTE_VIDEO_UNAVAILABLE, (event) => {
        let tipText = ''
        let withoutStreamUI = document.querySelector('.without-stream-ui')
        if (event.streamType === "main") {
          tipText = '主播关闭了摄像头'
        }
        withoutStreamUI.style.display = 'flex'
        this.$message.warning(tipText)
        const config = {
          userId: event.userId,
          streamType: event.streamType,
          view: 'remoteStreamArea'
        }
        this.trtc.stopRemoteVideo(config)
      });

      /**
       * 监听音频音量变化事件
       * @param {Object} event 事件对象
       * @param {Array} event.result 音量变化结果
       * @param {String} event.result.userId 用户 ID
       * @param {Number} event.result.volume 音量
       * 音频音量变化事件
       * 该事件会在音量变化时被抛出
       */
      this.trtc.on(TRTC.EVENT.AUDIO_VOLUME, (event) => {
        event.result.forEach(({userId, volume}) => {
          const isMe = userId === ""; // 当 userId 为空串时，代表本地麦克风音量。
          if (isMe) {
            console.log(`  我的音量: ${volume}`);
          } else {
            console.log(`  用户 ${userId} 的音量: ${volume}`);
          }
        });
        // 开启音量回调，并设置每 1000ms 触发一次事件
        this.trtc.enableAudioVolumeEvaluation(1000);
      });

      /**
       * 监听 SDK 的连接状态变化
       * @param {Object} event 事件对象
       * @param {String} event.prevState 之前的连接状态
       * @param {String} event.state 当前的连接状态
       * 该事件会在 SDK 的连接状态变化时被抛出
       */
      this.trtc.on(TRTC.EVENT.CONNECTION_STATE_CHANGED, (event) => {
        const prevState = event.prevState;
        const curState = event.state;
        console.log(`connection state changed: ${prevState} -> ${curState}`);
      });

      /**
       * 监听音频播放状态变化
       * @param {Object} event 事件对象
       * @param {String} event.userId 用户 ID
       * @param {String} event.state 音频播放状态
       * @param {String} event.reason 音频播放状态变化原因
       * 该事件会在音频播放状态变化时被抛出
       */
      this.trtc.on(TRTC.EVENT.AUDIO_PLAY_STATE_CHANGED, (event) => {
        console.log(
            `${event.userId} player is ${event.state} because of ${event.reason}`
        );
      });

      /**
       * 监听视频播放状态变化
       * @param {Object} event 事件对象
       * @param {String} event.userId 用户 ID
       * @param {String} event.streamType 视频流类型
       * @param {String} event.state 视频播放状态
       * @param {String} event.reason 视频播放状态变化原因
       * 该事件会在视频播放状态变化时被抛出
       */
      this.trtc.on(TRTC.EVENT.VIDEO_PLAY_STATE_CHANGED, (event) => {
        console.log(
            `${event.userId} ${event.streamType} video player is ${event.state} because of ${event.reason}`
        );
      });

      /**
       * 监听屏幕分享停止事件
       * @param {Object} event 事件对象
       * 屏幕分享停止事件
       */
      this.trtc.on(TRTC.EVENT.SCREEN_SHARE_STOPPED, () => {
        console.log("screen sharing was stopped");
      });

      /**
       * 监听设备变化事件
       * @param {Object} event 事件对象
       * @param {String} event.type 设备类型
       * @param {String} event.device.label 设备名称
       * @param {String} event.action 事件类型
       * 设备变化事件
       */
      this.trtc.on(TRTC.EVENT.DEVICE_CHANGED, (event) => {
        console.log(`${event.type}(${event.device.label}) ${event.action}`);
      });

      /**
       * 监听实时统计信息
       * @param {Object} statistics  statistics 信息
       * @param {Number} statistics.rtt  rtt
       * @param {Number} statistics.upLoss  上行丢包率
       * @param {Number} statistics.downLoss  下行丢包率
       * 该事件会在 SDK 获取到实时统计信息时被抛出
       */
      this.trtc.on(TRTC.EVENT.STATISTICS, (statistics) => {
        //   console.warn(statistics.rtt, statistics.upLoss, statistics.downLoss);
      });
    },
    // 获取userSig
    genTestUserSig(sdkAppId, userId, sdkSecretKey) {

      const SDKAPPID = sdkAppId;

      const EXPIRETIME = 604800;

      const SDKSECRETKEY = sdkSecretKey;

      // a soft reminder to guide developer to configure sdkAppId/SDKSecretKey
      if (SDKAPPID === '' || SDKSECRETKEY === '') {
        alert(
            'Please configure your SDKAPPID/SDKSECRETKEY in js/debug/GenerateTestUserSig.js'
        );
      }
      const generator = new LibGenerateTestUserSig(SDKAPPID, SDKSECRETKEY, EXPIRETIME);
      const userSig = generator.genTestUserSig(userId);
      return {
        sdkAppId: SDKAPPID,
        userSig: userSig
      };
    },
    // 主播与指定用户连麦
    interactiveChat(row) {
      this.socket.emit('interactiveChat', {
        username: row.username,
        roomId: this.nowItem.roomId
      })
    },
    // 切换用户角色
    async switchUserRole() {
      await this.trtc.switchRole(TRTC.TYPE.ROLE_ANCHOR);
      this.isUserCanOperate = true;
    },
    // 获取直播间观众列表
    getListenerList() {
      this.showListenerList = true;
      this.socket.emit('getRoomUserList', {roomId: this.nowItem.roomId})
    },
    // 加入房间
    async joinRoom() {
      const config = {
        roomId: this.nowItem.roomId,
        scene: "live",
        sdkAppId: this.sdkAppId,
        userId: 'user_' + this.nowUser.id,
        userSig: this.genTestUserSig(this.sdkAppId, 'user_' + this.nowUser.id, this.sdkSecretKey).userSig,
        role: this.nowUser.username === this.nowItem.username ? TRTC.TYPE.ROLE_ANCHOR : TRTC.TYPE.ROLE_AUDIENCE
      }
      try {
        await this.trtc.enterRoom(config);
        this.$message.success('进入房间成功')
      } catch (error) {
        console.log(error)
        this.$message.error('进入房间失败，请联系管理员');
      }
    },
    // 退出房间
    async exitRoom() {
      try {
        await this.trtc.exitRoom();
        this.$message.success('退出房间成功')
        this.socket.emit('leaveRoom', {username: this.nowUser.username, roomId: this.nowItem.roomId})
        await updateRoomView(this.nowItem.roomId, 1)
      } catch (error) {
        console.log(error)
        console.error("退出房间失败，请联系管理员");
      }
    },
    // 推出直播间按钮
    jumpToLastPage() {
      this.$router.push('/home/liveQA')
    },
    // 发送消息
    sendChatMessage() {
      if (this.chatContent.length <= 0 || this.chatContent === '') {
        this.$message.warning('请先输入要发送的内容')
        return;
      }

      this.socket.emit('sendMessage', {
        username: this.nowUser.username,
        message: this.chatContent,
        roomId: this.nowItem.roomId
      })

      // 如果还没有获取过canvas元素 先获取
      if (this.canvas === null) {
        let el = document.querySelector('.stream-area')  // canvas的父元素
        this.canvas = document.querySelector('.barrage-canvas')
        this.ctx = this.canvas.getContext('2d')
        this.canvas.setAttribute('width', el.offsetWidth) // 将canvas元素设置与父元素同宽
        this.canvas.setAttribute('height', el.offsetHeight) //将canvas元素设置与父元素同高
      }

      this.texts.push({
        txt: this.chatContent,
        x: this.canvas.width,
        y: Math.floor(Math.random() * (this.canvas.height - 20)) + 20,
        color: this.getRandomColor(),
      })

      this.chatContent = ''
      this.update();
    },
    // 开启/关闭实时摄像头
    async operateVideo() {
      let withoutStreamUI = document.querySelector('.without-stream-ui')
      if (this.isVideoOpen === false) {
        this.isVideoOpen = true;
        withoutStreamUI.style.display = 'none'
        try {
          await this.trtc.startLocalVideo({
            view: document.getElementById(this.domId), // 在 DOM 中的 elementId 为 localVideo 的标签上预览视频。
          });
          this.$message.success('开启摄像头推流成功')
        } catch (error) {
          console.log(error)
          this.$message.error('开启摄像头失败，请联系管理员')
        }
      } else {
        this.isVideoOpen = false;
        withoutStreamUI.style.display = 'flex'
        try {
          await this.trtc.stopLocalVideo();
          this.$message.success('关闭摄像头成功')
        } catch (error) {
          console.log(error)
          this.$message.error('关闭摄像头失败，请联系管理员')
        }
      }
    },
    // 开启关闭麦克风
    async operateMicrophone() {
      if (this.isMicrophoneOpen === false) {
        this.isMicrophoneOpen = true;
        try {
          await this.trtc.startLocalAudio();
          this.$message.success('开启麦克风成功')
        } catch (error) {
          console.log(error)
          this.$message.error('开启麦克风失败，请联系管理员')
        }
      } else {
        this.isMicrophoneOpen = false;
        try {
          await this.trtc.stopLocalAudio();
          this.$message.success('关闭麦克风成功')
        } catch (error) {
          console.log(error)
          this.$message.error('关闭麦克风失败，请联系管理员')
        }
      }
    },
    async operateShareScreen() {
      let withoutStreamUI = document.querySelector('.without-stream-ui')
      if (this.isShareScreenOpen === false) {
        this.isShareScreenOpen = true;
        withoutStreamUI.style.display = 'none'
        const config = {
          view: this.domId
        }
        try {
          await this.trtc.startScreenShare(config);
          this.$message.success('开启屏幕共享成功')
        } catch (error) {
          console.log(error)
          this.$message.error('开启屏幕共享失败，请联系管理员')
        }
      } else {
        this.isShareScreenOpen = false;
        withoutStreamUI.style.display = 'flex'
        try {
          await this.trtc.stopScreenShare();
          this.$message.success('关闭屏幕共享成功')
        } catch (error) {
          console.log(error)
          this.$message.error('关闭屏幕共享失败，请联系管理员')
        }
      }
    },
    getRandomColor() {
      return '#' + Math.floor(Math.random() * 0xffffff).toString(16);
    },
    // 实现弹幕动画更新
    update() {
      // 清除画布
      this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
      // 改变弹幕数组里每一条弹幕位置，x值减少就好
      for (let i = 0; i < this.texts.length; i++) {
        this.texts[i].x -= 1;
        // 若已移出视频很远了，删除弹幕数组元素
        if (this.texts[i].x < -500) this.texts.splice(i, 1);
      }
      // 绘制文字
      this.texts.forEach(item => {
        this.ctx.font = "20px Arial";
        //颜色
        this.ctx.fillStyle = item.color;
        //位置
        this.ctx.fillText(item.txt, item.x, item.y);
      })
      // 执行一个动画，并且要求浏览器在下次重绘之前调用指定的回调函数更新动画
      window.requestAnimationFrame(this.update);
    }
  }
}
</script>

<style lang="less" scoped>
@keyframes MoveIn {
  0% {
    opacity: 0;
    transform: translateY(12px);
  }

  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

.container {
  width: 100%;
  height: 100%;
  padding: 10px;

  .main-box {
    width: 100%;
    height: 94%;
    display: flex;
    animation: MoveIn .7s cubic-bezier(.4, 0, .2, 1);

    .left-stream-card {
      width: 65%;
      display: flex;
      flex-direction: column;

      /deep/ .el-card__body {
        width: 100%;
        height: 100%;
        display: flex;
        flex-direction: column;
        padding: 0;
      }

      .room-header {
        position: relative;
        display: flex;
        align-items: center;
        padding: 10px;

        .room-info {
          display: flex;
          flex-direction: column;
          margin-left: 15px;

          .room-title {
            position: relative;
            max-width: 24em;
            font-size: 24px;
            color: #3a3a3a;
            white-space: nowrap;
            text-overflow: ellipsis;
            overflow: hidden;
          }

          .room-creator {
            margin-top: 10px;
            color: #999;
          }
        }

        .exit-room-btn {
          position: absolute;
          right: 10px;
        }
      }

      .stream-area {
        flex: 1;
        width: 100%;
        margin-top: 5px;
        display: flex;

        .without-stream-ui {
          width: 100%;
          height: 100%;
          display: flex;
          justify-content: center;
          align-items: center;
          color: #999;
          background: rgb(0, 0, 0);

          .description-text {
            font-size: 28px;
            color: rgb(170, 170, 170);
            text-align: center;
            line-height: 50px;
            height: 50px;
            margin-bottom: 20px;
          }
        }

        .inner-stream-area {
          margin: 0 auto;
        }

        .barrage-canvas {
          pointer-events: none;
          position: absolute;
        }
      }

      .operate-area {
        margin-top: 5px;
        width: 100%;
        display: flex;
        padding: 10px;

        .operate-item {
          display: flex;
          flex-direction: column;
          align-items: center;
          cursor: pointer;

          .operate-item-icon {
            width: 30px;
            height: 30px;
          }

          .operate-item-name {
            margin-top: 8px;
            font-size: 14px;
          }
        }

        .operate-item:hover {
          color: #f80;
        }
      }
    }

    .width-blank {
      width: 10px;
    }

    .right-chat-card {
      flex: 1;
      height: 100%;
      display: flex;
      flex-direction: column;

      /deep/ .el-card__body {
        height: 100%;
        display: flex;
        flex-direction: column;
      }

      .chat-header {
        font-size: 18px;
        display: flex;
        padding-bottom: 5px;
        border-bottom: 1px solid #e0e0e0;;

        .listener-list {
          margin-left: auto;
        }
      }

      .chat-content {
        flex: 1;

        .single-chat {
          margin-top: 5px;
          animation: MoveIn .7s cubic-bezier(.4, 0, .2, 1);
        }
      }

      .chat-message-input {
        position: relative;

        .send-btn {
          position: absolute;
          right: 10px;
          bottom: 10px;
        }
      }
    }
  }

}
</style>